﻿using Linh.Mvvm;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ThinningImage
{
    public class MainWindowViewModel : BindableBase
    {
        private BitmapSource _OriginalSource;
        private BitmapSource _ProcessedSource;
        private char character;

        public MainWindowViewModel()
        {
            //var path = Environment.CurrentDirectory + "\\data\\" + (char)0x4E02 + ".png";
            //OriginalSource = new BitmapImage(new Uri(path, UriKind.RelativeOrAbsolute));
            //ProcessedSource = ConvertToGrayscale(OriginalSource);
        }

        public ICommand StartCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                });
            }
        }

        private WriteableBitmap ConvertToGrayscale(BitmapSource image)
        {
            WriteableBitmap bitmap = new WriteableBitmap(image);
            byte[] bytes = new byte[bitmap.PixelHeight * bitmap.PixelWidth * bitmap.Format.BitsPerPixel / 8];
            bitmap.CopyPixels(bytes, bitmap.PixelWidth * bitmap.Format.BitsPerPixel / 8, 0);

            byte[] converted = new byte[bitmap.PixelHeight * bitmap.PixelWidth];
            int index = 0;
            for (int i = 0; i < bitmap.PixelHeight; i++)
            {
                for (int j = 0; j < bitmap.PixelWidth; j++)
                {
                    byte r = bytes[(i * bitmap.PixelWidth + j) * 4];
                    byte g = bytes[(i * bitmap.PixelWidth + j) * 4 + 1];
                    byte b = bytes[(i * bitmap.PixelWidth + j) * 4 + 2];
                    byte a = bytes[(i * bitmap.PixelWidth + j) * 4 + 3];
                    if (a == 0)
                        converted[index++] = 255;
                    else converted[index++] = (byte)((r + b + g) > 384 ? 255 : 0);
                }
            }
            WriteableBitmap bitmap2 = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight, bitmap.DpiX, bitmap.DpiY, PixelFormats.Gray8, bitmap.Palette);
            bitmap2.WritePixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight), converted, bitmap.PixelWidth, 0);
            return bitmap2;
        }

        private BitmapSource Thinning(BitmapSource image)
        {
            ImageHelper output = new ImageHelper(image);
            for (int i = 0; i < image.PixelHeight; i++)
            {
                for (int j = 0; j < image.PixelWidth; j++)
                {
                    if (output[i, j].Value == 255)
                        output[i, j] = 0;
                    else output[i, j] = 1;
                }
            }
            bool kt = false;
            List<Point> list = new List<Point>();
            int counter = 0;
            do
            {
                list.Clear();
                kt = false;
                Debug.WriteLine(counter++);
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        if (output[i, j].Value == 0)
                            continue;
                        var p1 = output[i, j] ?? 0;
                        var p2 = output[i - 1, j] ?? 0;
                        var p3 = output[i - 1, j + 1] ?? 0;
                        var p4 = output[i, j + 1] ?? 0;
                        var p5 = output[i + 1, j + 1] ?? 0;
                        var p6 = output[i + 1, j] ?? 0;
                        var p7 = output[i + 1, j - 1] ?? 0;
                        var p8 = output[i, j - 1] ?? 0;
                        var p9 = output[i - 1, j - 1] ?? 0;
                        var n = N(p2, p3, p4, p5, p6, p7, p8, p9);
                        var s = S(p2, p3, p4, p5, p6, p7, p8, p9);
                        if (n >= 2 && n <= 6 && s == 1 && p2 * p4 * p6 == 0 && p4 * p6 * p8 == 0)
                        {
                            list.Add(new Point(i, j));
                            kt = true;
                        }
                    }
                }
                foreach (var item in list)
                {
                    output[(int)item.X, (int)item.Y] = 0;
                }
                list.Clear();
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        if (output[i, j].Value == 0)
                            continue;
                        var p1 = output[i, j] ?? 0;
                        var p2 = output[i - 1, j] ?? 0;
                        var p3 = output[i - 1, j + 1] ?? 0;
                        var p4 = output[i, j + 1] ?? 0;
                        var p5 = output[i + 1, j + 1] ?? 0;
                        var p6 = output[i + 1, j] ?? 0;
                        var p7 = output[i + 1, j - 1] ?? 0;
                        var p8 = output[i, j - 1] ?? 0;
                        var p9 = output[i - 1, j - 1] ?? 0;
                        var n = N(p2, p3, p4, p5, p6, p7, p8, p9);
                        var s = S(p2, p3, p4, p5, p6, p7, p8, p9);
                        if (n >= 2 && n <= 6 && s == 1 && p2 * p4 * p8 == 0 && p2 * p6 * p8 == 0)
                        {
                            list.Add(new Point(i, j));
                            kt = true;
                        }
                    }
                }
                foreach (var item in list)
                {
                    output[(int)item.X, (int)item.Y] = 0;
                }
            }
            while (kt);
            for (int i = 0; i < image.PixelHeight; i++)
            {
                for (int j = 0; j < image.PixelWidth; j++)
                {
                    if (output[i, j].Value == 1)
                        output[i, j] = 255;
                }
            }
            return output.ToImage();
        }

        private BitmapSource Skeletonization(BitmapSource image)
        {
            ImageHelper output = new ImageHelper(image);
            for (int i = 0; i < image.PixelHeight; i++)
            {
                for (int j = 0; j < image.PixelWidth; j++)
                {
                    if (output[i, j].Value == 255)
                        output[i, j] = 0;
                    else output[i, j] = 1;
                }
            }
            bool kt = false;
            List<byte> list = new List<byte>();
            int size = 1;
            do
            {
                kt = false;
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        list.Clear();
                        if (output[i, j].Value == 0)
                            continue;
                        for (int u = -size; u <= size; u++)
                        {
                            for (int v = -size; v <= size; v++)
                            {
                                //if (u * v != 0) continue;
                                if (output[i - u, j - v] != null)
                                    list.Add(output[i - u, j - v].Value);
                            }
                        }
                        byte min = Math.Min((byte)255, (byte)(list.Min() + 1));
                        if (output[i, j].Value != min)
                        {
                            kt = true;
                            output[i, j] = min;
                        }
                    }
                }
            }
            while (kt);
            ImageHelper output2 = new ImageHelper(image);
            for (int i = 0; i < image.PixelHeight; i++)
            {
                for (int j = 0; j < image.PixelWidth; j++)
                {
                    list.Clear();
                    if (output[i, j].Value == 0)
                    {
                        output2[i, j] = 0;
                        continue;
                    }
                    for (int u = -size; u <= size; u++)
                    {
                        for (int v = -size; v <= size; v++)
                        {
                            //if (u * v != 0) continue;
                            if (output[i - u, j - v] != null)
                                list.Add(output[i - u, j - v].Value);
                        }
                    }
                    byte max = Math.Min((byte)255, list.Max());
                    if (output[i, j].Value != max)
                    {
                        output2[i, j] = 0;
                    }
                    else output2[i, j] = output[i, j];
                }
            }
            do
            {
                kt = false;
                byte max = 0;
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        if (output2[i, j].Value > 1)
                        {
                            if (output2[i, j].Value > max)
                            {
                                max = output2[i, j].Value;
                            }
                            kt = true;
                            var newValue = (byte)(output2[i, j].Value - 1);
                            output2[i, j] = newValue;
                            for (int u = -size; u <= size; u++)
                            {
                                for (int v = -size; v <= size; v++)
                                {
                                    //if (u * v != 0) continue;
                                    if (output2[i - u, j - v].Value == 0)
                                    {
                                        output2[i - u, j - v] = newValue;
                                    }
                                }
                            }
                        }
                    }
                }
                Debug.WriteLine(max);
            } while (kt);
            for (int i = 0; i < image.PixelHeight; i++)
            {
                for (int j = 0; j < image.PixelWidth; j++)
                {
                    if (output2[i, j].Value == 1)
                    {
                        output2[i, j] = 255;
                    }
                }
            }
            return output2.ToImage();
        }

        private byte N(params byte[] points)
        {
            byte sum = 0;
            foreach (var item in points)
            {
                sum += item;
            }
            return sum;
        }

        private byte S(params byte[] points)
        {
            byte s = 0;
            var length = points.Length;
            for (int i = 0; i < length; i++)
            {
                if (points[i] > points[(i + 1) % length])
                    s++;
            }
            return s;
        }

        public char Character
        {
            get { return character; }
            set { SetProperty(ref character, value); }
        }

        public BitmapSource OriginalSource
        {
            get { return _OriginalSource; }
            set
            {
                SetProperty(ref _OriginalSource, value);
                ProcessedSource = ConvertToGrayscale(OriginalSource);
                ProcessedSource = Skeletonization(ProcessedSource);
            }
        }

        public BitmapSource ProcessedSource
        {
            get { return _ProcessedSource; }
            set { SetProperty(ref _ProcessedSource, value); }
        }
    }
}